#ifndef MATRIX3_H
#define MATRIX3_H
#include <math.h>
#include <iostream>
#include "Matrix2.h"
#include "Vector3.h"

using namespace std;

/*
*	___ Matrix3 Header___
*
*
**************************************************************
*/

struct MatrixElements4x4
{
		//   |e01 e02 e03 e04 |
		//   |e05 e06 e07 e08 |
		//	  |e09 e10 e11 e12 |
		//	  |e13 e14 e15 e16 |
	public: float e[4][4];
};

class Matrix3
{
	private:
		MatrixElements4x4 _elements;
		
	public:
		//Constructors
		
		Matrix3();

		Matrix3(const Matrix3 &mat);

		Matrix3( float e11,float e12,float e13,
					float e21,float e22,float e23,
					float e31,float e32,float e33);

		Matrix3( float e11,float e12,float e13, float e14,
					float e21,float e22,float e23, float e24,
					float e31,float e32,float e33, float e34,
					float e41,float e42,float e43, float e44 );

		Matrix3( MatrixElements4x4 elements );	
		
		//Getter/Setter
		MatrixElements4x4 getElements() const;

		void setElements( MatrixElements4x4 elements);

		//Overload Operators
		Matrix3 operator * (const Matrix3 &mat2) const;

		Matrix3 operator * (const float& scal) const;

		Vector3 operator * (const Vector3 &vec) const;

		Matrix3 operator + (const Matrix3 &mat2) const;

		Matrix3 operator - (const Matrix3 &mat2) const;

		void LoadIdentity();

		//Transformations
		static Matrix3 Scale( float xFactor, float yFactor, float zFactor )
		{
			Matrix3 mat(	xFactor,0.0,0.0,0.0,
								0.0,yFactor,0.0,0.0,
								0.0,0.0,zFactor,0.0,
								0.0,0.0,0.0,1.0 );
			return mat;
		}

		static Matrix3 Translate( float deltaX, float deltaY, float deltaZ )
		{
			Matrix3 mat(	1.0,0.0,0.0,deltaX,
								0.0,1.0,0.0,deltaY,
								0.0,0.0,1.0,deltaZ,
								0.0,0.0,0.0,1.0);
			return mat;
		}

		static Matrix3 RotateX( float rad )
		{
			float dCos = cos(rad);
			float dSin = sin(rad);

			Matrix3 mat(
								1.0,   0.0,  0.0,   0.0,
								0.0,   dCos, -dSin, 0.0,
								0.0,   dSin, dCos,  0.0,
								0.0,   0.0,  0.0,   1.0		
			);

			return mat;
		}

		static Matrix3 RotateY( float rad )
		{
			float dCos = cos(rad);
			float dSin = sin(rad);

			Matrix3 mat(
								dCos,  0.0,  dSin, 0.0,
								0.0,   1.0,  0.0,  0.0,
								-dSin, 0.0, dCos, 0.0,
								0.0,   0.0,  0.0,  1.0		
			);

			return mat;
		}

		static Matrix3 RotateZ( float rad )
		{
			float dCos = cos(rad);
			float dSin = sin(rad);

			Matrix3 mat(
								dCos, -dSin, 0.0, 0.0,
								dSin, dCos,  0.0, 0.0,
								0.0,  0.0,   1.0, 0.0,
								0.0,  0.0,   0.0, 1.0		
			);

			return mat;
		}

		/*
		* Rotates an Object in a given Axis Vector
		*/
		static Matrix3 Rotate( Vector3 axis, float rad )
		{
			float dCos = cos(rad);
			float dCos2 = 1-dCos;
			float dSin = sin(rad);

			Matrix3 mat(
								dCos + (axis.getX() * axis.getX() ) * dCos2,  
								( axis.getX() * axis.getY() ) * dCos2 - axis.getZ() * dSin,  
								( axis.getX() * axis.getZ() ) * dCos2 + axis.getY() * dSin,  
								0.0,

								( axis.getY() * axis.getX() ) * dCos2 + axis.getZ() * dSin,  
								dCos + ( axis.getY() * axis.getY() ) * dCos2,  
								( axis.getY() * axis.getZ() ) * dCos2 - axis.getX() * dSin,  
								0.0,

								( axis.getZ() * axis.getX() ) * dCos2 - axis.getY() * dSin,  
								( axis.getZ() * axis.getY() ) * dCos2 + axis.getX() * dSin,
								dCos + ( axis.getZ() * axis.getZ()) * dCos2,  
								0.0,

								0.0,  
								0.0,
								0.0,  
								1.0		
			);

			return mat;
		}

		float* getElementArray() const;
		
		Vector3 MultNormal( Vector3 normal ) const;
		Vector3 MultNormalNotNormalize( Vector3 normal ) const;

		float getDeterminant() const;

		Matrix3 getInverse() const;

		void InverseMatrix();

		friend ostream & operator<< (ostream &o, const Matrix3 &mat);
};


/***************************************************************/
#endif
